package com.atguigu.exam.service.impl;

import ch.qos.logback.core.joran.util.beans.BeanUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.exam.entity.*;
import com.atguigu.exam.mapper.PaperQuestionMapper;
import com.atguigu.exam.mapper.QuestionAnswerMapper;
import com.atguigu.exam.mapper.QuestionChoiceMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.AIService;
import com.atguigu.exam.service.QuestionService;
import com.atguigu.exam.utils.ExcelUtil;
import com.atguigu.exam.vo.AiGenerateRequestVo;
import com.atguigu.exam.vo.QuestionImportVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * 题目Service实现类
 * 实现题目相关的业务逻辑
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {
    @Autowired
    private QuestionAnswerMapper questionAnswerMapper;    //答案
    @Autowired
    private QuestionChoiceMapper questionChoiceMapper;    //选项
    @Autowired
    private PaperQuestionMapper paperQuestionMapper;      //试卷题目关联表
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private AIService aiService;

    /**
     * 分页查询题目列表（支持多条件筛选）
     *
     * RESTful API教学：
     * - URL：GET /api/questions/list
     * - 查询参数：通过@RequestParam接收URL查询参数
     * - 默认值：通过defaultValue设置参数默认值
     * - 可选参数：通过required = false设置可选参数
     *
     * MyBatis Plus分页教学：
     * - Page对象：封装分页信息（页码、每页大小、总数等）
     * - QueryWrapper：动态构建查询条件，避免SQL注入
     * - 条件构建：支持等值查询(eq)、模糊查询(like)、排序(orderBy)
     *
     * @param page 当前页码，从1开始，默认第1页
     * @param size 每页显示数量，默认10条
     * @param questionDto 查询条件
     * @return 封装的分页查询结果，包含题目列表和分页信息
     */
    public Page<Question> getQuestionList(Integer page, Integer size, QuestionDto questionDto) {
        Page<Question> Ipage = new Page<>(page, size);
        Page<Question> ppage = baseMapper.customPage(Ipage, questionDto);
        return ppage;
    }

    /**
     * 根据ID查询单个题目详情
     *
     * RESTful API教学：
     * - URL模式：GET /api/questions/{id}
     * - 路径参数：通过@PathVariable获取URL中的参数
     * - 语义化：URL直观表达资源和操作
     *
     * @param id 题目主键ID，通过URL路径传递
     * @return 题目详细信息，包含选项和答案
     */
    public Question getQuestionById(Long id) {
        Question question = baseMapper.getQuestionById(id);
        //开启新线程来执行这个redis缓存操作
        new Thread(() -> {
            redisTemplate.opsForZSet().incrementScore("question:popular", id, 1.0);
        }).start();
        redisTemplate.opsForValue().set("question:" + id, question);
        return question;
    }

    /**
     * 创建新题目
     *
     * RESTful API教学：
     * - HTTP方法：POST表示创建操作
     * - 请求体：通过@RequestBody接收JSON格式的请求体
     * - 数据绑定：Spring自动将JSON转换为Java对象
     *
     * 事务处理：
     * - 题目创建涉及多张表（题目、选项、答案）
     * - Service层方法应该使用@Transactional保证数据一致性
     *三张表   question 题目表
     *        questionanswers  答案表
     *        questionchoices  选项表
     * @param question 前端提交的题目数据（JSON格式）
     * @return 创建成功后的题目信息
     */
    @Transactional
    public boolean createQuestion(Question question) {
        LambdaQueryWrapper<Question> lqwQuestion = new LambdaQueryWrapper<>();
        //判断题目的类型跟标题是否重复
        lqwQuestion.eq(Question::getType, question.getType());
        lqwQuestion.eq(Question::getTitle, question.getTitle());
        boolean exists = baseMapper.exists(lqwQuestion);
        if (exists) {
            //题目已经存在
            throw new RuntimeException("%s类别的题目%s已经存在".formatted(question.getType(), question.getTitle()));
        }
        //题目没有存在,可以添加
        int insert = baseMapper.insert(question);
        if (insert < 0) {
            throw new RuntimeException("添加题目失败");
        }
        //获取答案对象
        QuestionAnswer answer = question.getAnswer();
        //赋予答案的题目id
        answer.setQuestionId(question.getId());
        //判断题目是否是选择题
        if ("CHOICE".equals(question.getType())) {
            StringBuilder sb = new StringBuilder();
            //获取所有选项
            List<QuestionChoice> choices = question.getChoices();
            for (int i = 0; i < choices.size(); i++) {
                //取出选项
                QuestionChoice choice = choices.get(i);
                //排序
                choice.setSort(i);
                //赋予选项的题目id
                choice.setQuestionId(question.getId());
                //为选项表添加选项内容
                questionChoiceMapper.insert(choice);
                if (choice.getIsCorrect()) {
                    if (sb.length() > 0) {
                        sb.append(",");
                    }
                    sb.append((char) ('A' + i));
                }
            }
            answer.setAnswer(sb.toString());
        }
        //保存答案对象
        int insert1 = questionAnswerMapper.insert(answer);
        return insert1 > 0;
    }

    /**
     * 更新题目信息
     *
     * RESTful API教学：
     * - HTTP方法：PUT表示更新操作
     * - URL设计：PUT /api/questions/{id} 语义明确
     * - 参数组合：路径参数(ID) + 请求体(数据)
     *
     * @param question 更新的题目数据
     * @return 更新后的题目信息
     */
    @Transactional
    public boolean updateQuestion(Question question) {
        LambdaQueryWrapper<Question> lqwQuestion = new LambdaQueryWrapper<>();
        lqwQuestion.eq(Question::getType, question.getType());
        lqwQuestion.eq(Question::getTitle, question.getTitle());
        lqwQuestion.ne(Question::getId, question.getId());
        boolean exists = baseMapper.exists(lqwQuestion);
        if (exists) {
            //题目已经存在不能使用
            throw new RuntimeException("%s类别的题目%s已经存在".formatted(question.getType(),
                    question.getTitle()));

        } else {
            //可以修改了
            QuestionAnswer answer = question.getAnswer();
            //判断是不是选择题
            if ("CHOICE".equals(question.getType())) {
                StringBuilder sb = new StringBuilder();
                //获取所有选择题的选项
                List<QuestionChoice> choices = question.getChoices();
                //根据题目id删除选项表中的数据
                questionChoiceMapper.delete(new LambdaQueryWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId, question.getId()));
                //重新赋值选项表到选项表中
                for (int i = 0; i < choices.size(); i++) {
                    //取出选项
                    QuestionChoice choice = choices.get(i);
                    //id
                    choice.setId(null);
                    //创建时间
                    choice.setCreateTime(null);
                    //更新时间
                    choice.setUpdateTime(null);
                    //排序
                    choice.setSort(i);
                    //赋予选项的题目id
                    choice.setQuestionId(question.getId());
                    //为选项表添加选项内容
                    questionChoiceMapper.insert(choice);
                    if (choice.getIsCorrect()) {
                        if (sb.length() > 0) {
                            sb.append(",");
                        }
                        sb.append((char) ('A' + i));
                    }
                }
                answer.setAnswer(sb.toString());
            }
            //为答案表赋值
            int insert = questionAnswerMapper.updateById(answer);
            return insert > 0;
        }
    }

    /**
     * 删除题目
     *
     * RESTful API教学：
     * - HTTP方法：DELETE表示删除操作
     * - 响应设计：删除成功返回确认消息，失败返回错误信息
     *
     * 注意事项：
     * - 删除前应检查题目是否被试卷引用
     * - 考虑使用逻辑删除而非物理删除，保留数据完整性
     *
     * @param id 要删除的题目ID
     * @return 删除操作结果
     */
    public boolean deleteQuestion(Long id) {
        LambdaQueryWrapper<PaperQuestion> paperQuestionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        paperQuestionLambdaQueryWrapper.eq(PaperQuestion::getQuestionId, id);
        //题目与试卷关联
        Long l = paperQuestionMapper.selectCount(paperQuestionLambdaQueryWrapper);
        if (l > 0) {
            throw new RuntimeException("该题目被试卷引用，不能删除");
        }
        //删除主表
        boolean b = removeById(id);
        //删除选项表
        int delete = questionChoiceMapper.delete(
                new LambdaQueryWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId, id)
        );
        //删除答案表
        int delete1 = questionAnswerMapper.delete(
                new LambdaQueryWrapper<QuestionAnswer>().eq(QuestionAnswer::getQuestionId, id)
        );
        if (b && delete > 0 && delete1 > 0) {
            return true;
        } else {
            return false;
        }

    }


    /**
     * 获取热门题目 - 首页展示推荐题目
     *
     * 业务逻辑：
     * - 热门度定义：按创建时间倒序，展示最新题目
     * - 可扩展：未来可按答题次数、正确率等指标排序
     *
     * SQL优化教学：
     * - 使用LIMIT限制结果集大小，提高查询性能
     * - 建议在create_time字段上建立索引
     *
     * @param size 返回题目数量，默认6条（适合首页展示）
     * @return 热门题目列表
     */
    /**
     * 获取热门题目 - 基于访问次数的推荐
     *
     * 业务逻辑：
     * - 热门度定义：根据题目被访问的次数排序
     * - 实现方式：使用Redis Sorted Set记录访问次数
     * - 数据来源：用户查看题目详情时自动记录
     *
     * 技术亮点：
     * - 使用Redis Sorted Set高效存储和排序
     * - 异步增加访问计数，不影响用户体验
     * - 缓存热门题目列表，提高查询性能
     *
     * @param size 返回题目数量，默认10条
     * @return 热门题目列表
     */
    public List<Question> getPopularQuestions(Integer size) {
        Set<Object> redisQuestionIds = redisTemplate.opsForZSet().reverseRange("question:popular", 0, size - 1);
        List<Question> questions = new ArrayList<>();
        // 只有当redisQuestionIds不为空时才执行查询
        if (redisQuestionIds != null && !redisQuestionIds.isEmpty()) {
            questions = baseMapper.selectList(
                    new LambdaQueryWrapper<Question>().in(Question::getId, redisQuestionIds)
            );
        }
        //将题目反转
        //创建一个前端需要的热门题目集合
//        List<Question> returnList = new ArrayList<>();
        //获取redis中的热门数据数量
        int redisSize = redisQuestionIds.size();
        //计算redis中缺少的数据量
        int diff = size - redisSize;
        if (diff > 0) {
            //redis数量不够用，从数据库中获取并添加到returnList中
            LambdaQueryWrapper<Question> lqwQuestion = new LambdaQueryWrapper<>();
            lqwQuestion.orderByDesc(Question::getCreateTime);
            //题目中不包含redis中有的
            lqwQuestion.notIn(redisQuestionIds != null && !redisQuestionIds.isEmpty(), Question::getId, redisQuestionIds);
            //在整个数据库语句末尾添加limit(获取差的条数)
            lqwQuestion.last("limit " + diff);
            List<Question> questions1 = baseMapper.selectList(lqwQuestion);
            questions.addAll(questions1);
        }
        return questions;
    }

    /**
     * 从Excel文件批量导入题目
     * @param file Excel文件
     * @return 导入结果
     */
    public String importFromExcel(MultipartFile file) throws  Exception{
        //校验
        if (file == null || file.isEmpty()){
            throw new RuntimeException("文件不能为空");
        }
        String filename = file.getOriginalFilename();
        if (!filename.endsWith(".xls") && !filename.endsWith(".xlsx")){
            throw new RuntimeException("批量导入的文件格式错误，必须是xls或者xlsx格式！");
        }
        //2.解析文件
        List<QuestionImportVo> questionImportVoList = ExcelUtil.parseExcel(file);
        //3.调用批量导入业务
        int successCount = importQuestions(questionImportVoList);
        String result = "批量导入题目接口调用成功！ 一共：%s 题目需要导入，成功导入了：%s 道题！".formatted(questionImportVoList.size(),successCount);
        return result;
    }

    /**
     * 批量导入题目（通用接口，支持Excel导入或AI生成后的确认导入）
     * @param questions 题目导入DTO列表
     * @return 导入结果
     */
    @Transactional
    public Integer importQuestions(List<QuestionImportVo> questions) {
        //判断文件是不是空的
        if (questions == null && questions.isEmpty()) {
            throw new RuntimeException("文件不能为空");
        }
        //开始保存
        int sucsessCount = 0;
        for (int i = 0; i < questions.size(); i++) {
            try {
                Question question = convert(questions.get(i));
                //单体保存
                createQuestion(question);
                sucsessCount++;
            } catch(Exception e){
                log.debug("{}题目导入失败！",questions.get(i).getTitle());
            }
        }
        return sucsessCount;
    }

    @Override
    public List<QuestionImportVo> generateQuestionsByAi(AiGenerateRequestVo request) {
        //1.根据请求参数生成提示词
        String prompt = buildPrompt(request);
        //2.调用Kimi的API返回AI生成的结果
        String content = aiService.callKimiAi(prompt);
        //4. 结果内容解析
    /*
           ```json
              {
                 questions:[{},{},{}]
              }
           ```
         */
        int startIndex = content.indexOf("```json");
        int endIndex = content.lastIndexOf("```");
        //保证有数据，且下标正确！
        if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) {
            //获取真正结果
            String realResult = content.substring(startIndex+7,endIndex);
            System.out.println("realResult = " + realResult);
            JSONObject jsonObject = JSONObject.parseObject(realResult);
            JSONArray questions = jsonObject.getJSONArray("questions");
            List<QuestionImportVo> questionImportVoList = new ArrayList<>();
            for (int i = 0; i < questions.size(); i++) {
                //获取对象
                JSONObject questionJson = questions.getJSONObject(i);
                QuestionImportVo questionImportVo = new QuestionImportVo();
                questionImportVo.setTitle(questionJson.getString("title"));
                questionImportVo.setType(questionJson.getString("type"));
                questionImportVo.setMulti(questionJson.getBoolean("multi"));
                questionImportVo.setDifficulty(questionJson.getString("difficulty"));
                questionImportVo.setScore(questionJson.getInteger("score"));
                questionImportVo.setAnalysis(questionJson.getString("analysis"));
                questionImportVo.setCategoryId(request.getCategoryId());

                //选择题处理选项
                if ("CHOICE".equals(questionImportVo.getType())) {
                    JSONArray choices = questionJson.getJSONArray("choices");
                    List<QuestionImportVo.ChoiceImportDto> choiceImportDtoList = new ArrayList<>(choices.size());
                    for (int i1 = 0; i1 < choices.size(); i1++) {
                        JSONObject choicesJSONObject = choices.getJSONObject(i1);
                        QuestionImportVo.ChoiceImportDto choiceImportDto = new QuestionImportVo.ChoiceImportDto();
                        choiceImportDto.setContent(choicesJSONObject.getString("content"));
                        choiceImportDto.setIsCorrect(choicesJSONObject.getBoolean("isCorrect"));
                        choiceImportDto.setSort(choicesJSONObject.getInteger("sort"));
                        choiceImportDtoList.add(choiceImportDto);
                    }
                    questionImportVo.setChoices(choiceImportDtoList);
                }
                //答案 [判断题！ TRUE |FALSE  false true  f  t 是 否]
                questionImportVo.setAnswer(questionJson.getString("answer"));
                questionImportVoList.add(questionImportVo);
            }
            return questionImportVoList;
        }
        throw new RuntimeException("ai生成题目json数据结构错误，无法正常解析！数据为：%s".formatted(content));
    }

    @Override
    public String buildPrompt(AiGenerateRequestVo request) {
        StringBuilder prompt = new StringBuilder();

        prompt.append("请为我生成").append(request.getCount()).append("道关于【")
                .append(request.getTopic()).append("】的题目。\n\n");

        prompt.append("要求：\n");

        // 题目类型要求
        if (request.getTypes() != null && !request.getTypes().isEmpty()) {
            List<String> typeList = Arrays.asList(request.getTypes().split(","));
            prompt.append("- 题目类型：");
            for (String type : typeList) {
                switch (type.trim()) {
                    case "CHOICE":
                        prompt.append("选择题");
                        if (request.getIncludeMultiple() != null && request.getIncludeMultiple()) {
                            prompt.append("(包含单选和多选)");
                        }
                        prompt.append(" ");
                        break;
                    case "JUDGE":
                        prompt.append("判断题（**重要：确保正确答案和错误答案的数量大致平衡，不要全部都是正确或错误**） ");
                        break;
                    case "TEXT":
                        prompt.append("简答题 ");
                        break;
                }
            }
            prompt.append("\n");
        }

        // 难度要求
        if (request.getDifficulty() != null) {
            String difficultyText = switch (request.getDifficulty()) {
                case "EASY" -> "简单";
                case "MEDIUM" -> "中等";
                case "HARD" -> "困难";
                default -> "中等";
            };
            prompt.append("- 难度等级：").append(difficultyText).append("\n");
        }

        // 额外要求
        if (request.getRequirements() != null && !request.getRequirements().isEmpty()) {
            prompt.append("- 特殊要求：").append(request.getRequirements()).append("\n");
        }

        // 判断题特别要求
        if (request.getTypes() != null && request.getTypes().contains("JUDGE")) {
            prompt.append("- **判断题特别要求**：\n");
            prompt.append("  * 确保生成的判断题中，正确答案(TRUE)和错误答案(FALSE)的数量尽量平衡\n");
            prompt.append("  * 不要所有判断题都是正确的或都是错误的\n");
            prompt.append("  * 错误的陈述应该是常见的误解或容易混淆的概念\n");
            prompt.append("  * 正确的陈述应该是重要的基础知识点\n");
        }

        prompt.append("\n请严格按照以下JSON格式返回，不要包含任何其他文字：\n");
        prompt.append("```json\n");
        prompt.append("{\n");
        prompt.append("  \"questions\": [\n");
        prompt.append("    {\n");
        prompt.append("      \"title\": \"题目内容\",\n");
        prompt.append("      \"type\": \"CHOICE|JUDGE|TEXT\",\n");
        prompt.append("      \"multi\": true/false,\n");
        prompt.append("      \"difficulty\": \"EASY|MEDIUM|HARD\",\n");
        prompt.append("      \"score\": 5,\n");
        prompt.append("      \"choices\": [\n");
        prompt.append("        {\"content\": \"选项内容\", \"isCorrect\": true/false, \"sort\": 1}\n");
        prompt.append("      ],\n");
        prompt.append("      \"answer\": \"TRUE或FALSE(判断题专用)|文本答案(简答题专用)\",\n");
        prompt.append("      \"analysis\": \"题目解析\"\n");
        prompt.append("    }\n");
        prompt.append("  ]\n");
        prompt.append("}\n");
        prompt.append("```\n\n");

        prompt.append("注意：\n");
        prompt.append("1. 选择题必须有choices数组，判断题和简答题设置answer字段\n");
        prompt.append("2. 多选题的multi字段设为true，单选题设为false\n");
        prompt.append("3. **判断题的answer字段只能是\"TRUE\"或\"FALSE\"，请确保答案分布合理**\n");
        prompt.append("4. 每道题都要有详细的解析\n");
        prompt.append("5. 题目要有实际价值，贴近实际应用场景\n");
        prompt.append("6. 严格按照JSON格式返回，确保可以正确解析\n");

        // 如果只生成判断题，额外强调答案平衡
        if (request.getTypes() != null && request.getTypes().equals("JUDGE") && request.getCount() > 1) {
            prompt.append("7. **判断题答案分布要求**：在").append(request.getCount()).append("道判断题中，");
            int halfCount = request.getCount() / 2;
            if (request.getCount() % 2 == 0) {
                prompt.append("请生成").append(halfCount).append("道正确(TRUE)和").append(halfCount).append("道错误(FALSE)的题目");
            } else {
                prompt.append("请生成约").append(halfCount).append("-").append(halfCount + 1).append("道正确(TRUE)和约").append(halfCount).append("-").append(halfCount + 1).append("道错误(FALSE)的题目");
            }
        }
        return prompt.toString();
    }

    //数据类型转换  QuestionImportVo --> Question
    public Question convert(QuestionImportVo questionImportVo) {
        Question question = new Question();
        BeanUtils.copyProperties(questionImportVo, question);
        //判断是不是选择题
        if ("CHOICE".equals(questionImportVo.getType())) {
            if (questionImportVo.getChoices().size() > 0) {
                List<QuestionChoice> questionChoices = new ArrayList<>(
                        questionImportVo.getChoices().size()
                );
                for (QuestionImportVo.ChoiceImportDto importVoChoice : questionImportVo.getChoices()) {
                    QuestionChoice questionChoice = new QuestionChoice();
                    questionChoice.setContent(importVoChoice.getContent());
                    questionChoice.setIsCorrect(importVoChoice.getIsCorrect());
                    questionChoice.setSort(importVoChoice.getSort());
                    questionChoices.add(questionChoice);
                }
                question.setChoices(questionChoices);
            }
        }
        //不管是不是选择题都得设置答案
        QuestionAnswer questionAnswer = new QuestionAnswer();
        //判断需要将答案转化为大写
        if ("JUDGE".equals(questionImportVo.getType())) {
            questionAnswer.setAnswer(questionImportVo.getAnswer().toUpperCase());
        } else {
            questionAnswer.setAnswer(questionImportVo.getAnswer());
        }
        questionAnswer.setKeywords(questionImportVo.getKeywords());
        question.setAnswer(questionAnswer);
        return question;
    }

} 